# coding=utf8

import pandas as pd
import numpy as np


class Preliminary:

    df = pd.read_csv("stu92.csv", header=0, index_col=False)

    df2 = pd.DataFrame(
        data=[["甲", 2, np.nan, 4],
              ["甲", 3, np.nan, 4],
              [np.nan, np.nan, np.nan, None],
              ["丁", 5, np.nan, 3]],
        columns=list("abcd"))

    @classmethod
    def df_isna_notna(cls):
        """展示使用isna和notna检测缺失值和非缺失值"""

        print(
            "# 使用isna检测缺失值\n"
            " >>> df\n"
            f"{cls.df}\n"
            " >>> pd.isna(df)\n"
            f"{pd.isna(cls.df)}\n"
        )

        def fun_fill(x, y=0):
            return y if pd.isna(x) else x

        print(
            "# 使用isna和applymap组合替换缺失值为0\n"
            " >>> fun_fill = lambda x, y=0: y if pd.isna(x) else x\n"
            f" >>> df.applymap(func=fun_fill)\n"
            f"{cls.df.applymap(func=fun_fill)}"
        )

        """
        使用notna检测非缺失值
        """
        print(
            "\n# 使用notna检测非缺失值\n"
            " >>> df2\n"
            f"{cls.df2}\n"
            f"# 检测整个数据集非缺失值情况\n"
            f" >>> df2.notna()\n"
            f"{cls.df2.notna()}\n"
            f"# 检测某个列, 提取列值为非缺失值的数据\n"
            f" >>> df2.loc[df2.d.notna(), :]\n"
            f"{cls.df2.loc[cls.df2.d.notna(), :]}\n"
        )

    @classmethod
    def df_fillna_with_value(cls):
        """使用fillna函数填充缺失值"""
        df = cls.df
        df = df.astype({"testday": np.datetime64})
        # raise error by pd.Timestamp
        # df.astype({'testday': pd.Timestamp})
        dffill = df.fillna(100)
        print(
            ">>>df\n"
            f"{df}\n\n"
            f">>> df.dtypes\n"
            f"{df.dtypes}\n\n"
            f"# 使用数字值填充所有数据\n"
            f">>> df.fillna(100)\n"
            f"{dffill}\n\n"
            f"# 填充不同类型值后，日期类型变为object类型\n"
            f">>> df.fillna(100).dtypes\n"
            f"{dffill.dtypes}\n"
        )
        # print(df2.dtypes)

    @classmethod
    def fillna_with_stat_value(cls):
        """使用统计数字特征值填充缺失值"""

        df = cls.df2
        print(
            ">>> df\n"
            f"{df}\n"
        )
        print(
            "# 使用均值、中位数、众数分别填充缺失值（fill with mean, median, mode at columns）\n"
            ">>> df.fillna({'a': df['a'].mode()[0], 'b': df['b'].mean(), 'd': df['d'].median()})\n"
            f"{df.fillna({'a': df['a'].mode()[0], 'b': df['b'].mean(), 'd': df['d'].median()})}"
        )
        print(
            "# 如果所有值均为缺失值时，填充会触发异常（filling with mean raise Error if all values are NaN）\n"
            ">>> cls.df.fillna({'c': df.c.median()})\n"
            f"RuntimeWarning: Mean of empty slice return np.nanmean(a, axis, out=out, keepdims=keepdims)"
        )

    @classmethod
    def fillna_by_filling_methods(cls):
        """使用前向、后向方式填充缺失值"""

        df = cls.df2
        print(
            "# ffill\n"
            ">>> df\n"
            f"{df}\n"
            f"# 使用向前填充方式填充（ffill）\n"
            f">>> df.fillna(method='ffill')\n"
            f"{df.fillna(method='ffill')}\n"
            "# 使用向后填充方式填充（bfill）\n"
            f">>> df.fillna(method='bfill')\n"
            f"{df.fillna(method='bfill')}\n"
        )


def task():
    df = pd.read_csv("stu92.csv",
                     header=0,
                     index_col=False)
    # df.testday = df.testday.apply(pd.to_datetime)

    print("缺失值情况：")
    print(df.isna())

    print("使用‘匿名’填充name列缺失值：\n >>> df.name = df.name.fillna('匿名')\n >>> df")
    df.name = df.name.fillna('匿名')
    print(df)

    print("使用均值填充age列缺失值：\n >>> df.age = df.age.fillna(df.age.mean())\n >>>df")
    df.age = df.age.fillna(df.age.mean())
    print(df)

    print("使用向前填充方式填充classno列缺失值：\n >>> df.classno = df.classno.fillna(method='ffill')\n >>>df")
    df.classno = df.classno.fillna(method='ffill')
    print(df)

    print("使用‘无’填充other列缺失值：\n >>> df.other = df.other.fillna('无')\n >>> df")
    df.other = df.other.fillna('无')
    print('填充后结果：')
    print(df)

    print("删除含有缺失值的列：\n >>> df = df.dropna(axis=1)\n >>> df")
    df.dropna(axis=1, inplace=True)
    print(df)


if __name__ == "__main__":
    # Preliminary.df_isna_notna()
    Preliminary.df_fillna_with_value()
    # Preliminary.fillna_with_numerical_characteristics()
    # Preliminary.fillna_by_filling_methods()
    # task()
